package com.example.springjwt.auth;

import com.example.springjwt.model.User;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import io.jsonwebtoken.*;

import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;

import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;

@Component
public class JwtUtil {

	private long accessTokenValidity = 60 * 60 * 1000;
	PrivateKey privKey;
	PublicKey pubKey;
	private final JwtParser jwtParser;

	private final String TOKEN_HEADER = "Authorization";
	private final String TOKEN_PREFIX = "Bearer ";

	public JwtUtil() throws Throwable {
		String privateKey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCfUInBBeDRD0diQ0aWORxAp3OCARp4dTlPrjLrg5gVmpjhYlwC3Wj3ePYKMcELnkdQS071K/YmYEf11+dB7uIj2AtKOjY+BWXKA4ZOv7Cs4mtU0r7HYQz7Fj0v8BWHjrEsGSBlH0+2bqJ3XPA4npju3RNjmdqdy1bhsS/DtCBfxIRfLtRKwSSJYvMIw7tivey2BmCj2XwnGvIaCCmMajSrEw53Bl56AhLM+mAKUoMTm3OaPlwXmy3/AnjhqIRsnf+ZPHg+bfSE7bvawZjAp4UxrO8aMZSiwp9mJssPQQtynv4p8ICuFDSoNLNwEACqe4PmGjlQFxQ3Mg4iftQSSmjHAgMBAAECggEBAJ2zUnjwgadQMYAySigf5jiVyUASKYq9tvqJ3DvHan9QJi9MdvobA3GcmihjMc13Yu43bbRBTQqV2gWgL3nTpqSGkj5quxDPtP+wV32SQKgSBN7VMiYYiontbKoMRLOH6xdU0/1lbjmO8GGpbN0PGSGxUyUPwhjalGkaRMa7giRoydoS/zezLuJHZunNqruOWcN09L6hcEQj2YT4kx1RiiYNGy9KzUGLYVZ1mbkUJI72sFAZrTlDNWM7VofxC2n405ls3uiG4eoBOatAyp2uGIBQlt8Ju9O7mrtyrSiQCAhbKyLEoCYi4xD3w3AbprgSxWV2q/BoJLwUblJ1YIrJnKkCgYEA0oMexF3ALlq7b0+B5GeNmbCt8QplskIQ7Yf5ccwOc1pPA3yuM+Eht8H7ZmBurPhBo5wVC8GefHn0GQ68zuVSNLUD4Efnn4Kzz50MGrFpnT6AHJPCeAEY8Mh0T9q/B76WSR7XvScAbfF09f7we2rWpdPgu0/4TkBqgTeSrjIh8YUCgYEAwb1TnJOR8RL2gx9/CvOPfCcQCeZ7GO0j4eVvj3kmr2fKa0FJ6iOX+bUSghF6PwHrtI5QqOlyKBSrljeCpRKpfPovAMqd02m0x7+4vpz0NOYMvT+0VAao0Oh0r47azxJWvaVwumy9iCbeSCJxqimFFEZeeF1yZGHVpAHUx1oxXNsCgYAbniQtMveBnVm10sKByqwjG7DWTLzm1BAQeaRaL6E9tP1kwbjBCao4x3RKDCC5BKDn7BQ+xJocToudtfdDVYahb5YgmI5kuOsKM9pgriHjBU4dca0zvUEeZc4Mo/An/fMNiMRZBoLh6amklFvMdUnP26pCKM20xnKynS3bOKyHWQKBgQCZ8kM6K9jP4u2XC7P7k8X0NWX9N/Ogvu7AVXvsWaxvvN2PlHWTY20jsspeKF0nK/OLqXpWfgmD58jguINLgCTFdldbHaWDlH9eKe43KKi0UAjXaIN2MUR7m/r8dVtRJ5VdZNTKjhWc1qXSU+xV9mAU2EXBByAau9LzW7EkJ3sJ+QKBgQCb2R8OkVoWLVUemP7Os0TWOI4VivHN+gA6nI9FbXvepra4QQCIOWlVUk+nFp+7jz+tn7d0wWNmTSx4s4H6v6j9x2rSYNIWPmqcOgdizniNXIcEaj1QAyWRyUsRVGVDMjeTSiHPG+Gw3Vij0heOTU3aEm46C00eyidxW48Y94S3AA==";
		String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn1CJwQXg0Q9HYkNGljkcQKdzggEaeHU5T64y64OYFZqY4WJcAt1o93j2CjHBC55HUEtO9Sv2JmBH9dfnQe7iI9gLSjo2PgVlygOGTr+wrOJrVNK+x2EM+xY9L/AVh46xLBkgZR9Ptm6id1zwOJ6Y7t0TY5nanctW4bEvw7QgX8SEXy7USsEkiWLzCMO7Yr3stgZgo9l8JxryGggpjGo0qxMOdwZeegISzPpgClKDE5tzmj5cF5st/wJ44aiEbJ3/mTx4Pm30hO272sGYwKeFMazvGjGUosKfZibLD0ELcp7+KfCArhQ0qDSzcBAAqnuD5ho5UBcUNzIOIn7UEkpoxwIDAQAB";
		String publicKeyR = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1GRIRMttmeiBRMWKsipu8ipaq8T4EjHav6FEF0zUHJbv+Y9+Bv6IHeB4u5Z3+ebWoxIlHR1sNcThqicnNz23fa8d6J3YAbosaWdoT+Da6KPdgYAW7NOGIQAwV+4f/HCDPe7S3GKFpeYrXnD9eEdPqxr61wqgQ7Qh9Jud06BHfErKf+XJc5QtRZZweXwwEaBArxyAXlUEbnKyOj8fgilA20LQukrY26/jsWQk24MqppH/QJDIieZ/y8fzO1yAoei94EBPY3/LG8xo4O7OoLVMGogUOcRyvcDjShHNtjTqEERw4tR32VTPxifDkXgSPC9c2TJXpF+dSCqgmgTz4yHmQIDAQAB";
		byte[] encoded = Base64.getDecoder().decode(privateKey);

		// PKCS8 decode the encoded RSA private key

		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
		KeyFactory kf = KeyFactory.getInstance("RSA");
		privKey = kf.generatePrivate(keySpec);

		byte[] pubencoded = Base64.getDecoder().decode(publicKey);

		X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(pubencoded);
		pubKey = kf.generatePublic(X509publicKey);
		this.jwtParser = Jwts.parser().setSigningKey(pubKey);
	}

	public String createToken(User user) throws Throwable {
		Claims claims = Jwts.claims().setSubject(user.getEmail());
		claims.put("firstName", user.getFirstName());
		claims.put("lastName", user.getLastName());
		
		
		List<String> ps = user.getPermissions();
		  ObjectMapper objectMapper = new ObjectMapper();

          String json = objectMapper.writeValueAsString(ps);
          claims.put("permissions", json);
		
		Date tokenCreateTime = new Date();
		Date tokenValidity = new Date(tokenCreateTime.getTime() + TimeUnit.MINUTES.toMillis(accessTokenValidity));
		return Jwts.builder().setClaims(claims).setExpiration(tokenValidity).signWith(SignatureAlgorithm.RS256, privKey)
				.compact();
	}

	private Claims parseJwtClaims(String token) {
		return jwtParser.parseClaimsJws(token).getBody();
	}

	public Claims resolveClaims(HttpServletRequest req) {
		try {
			String token = resolveToken(req);
			if (token != null) {
				return parseJwtClaims(token);
			}
			return null;
		} catch (ExpiredJwtException ex) {
			req.setAttribute("expired", ex.getMessage());
			throw ex;
		} catch (Exception ex) {
			req.setAttribute("invalid", ex.getMessage());
			throw ex;
		}
	}

	public String resolveToken(HttpServletRequest request) {

		String bearerToken = request.getHeader(TOKEN_HEADER);
		if (bearerToken != null && bearerToken.startsWith(TOKEN_PREFIX)) {
			return bearerToken.substring(TOKEN_PREFIX.length());
		}
		return null;
	}

	public boolean validateClaims(Claims claims) throws AuthenticationException {
		try {
			return claims.getExpiration().after(new Date());
		} catch (Exception e) {
			throw e;
		}
	}

	public String getEmail(Claims claims) {
		return claims.getSubject();
	}

	private List<String> getRoles(Claims claims) {
		return (List<String>) claims.get("roles");
	}

	static public void main(String[] ss) throws Throwable {
		JwtUtil util = new JwtUtil();
		User user = new User("123456@qq.com", "123456");
		
		user.setFirstName("yuqi");
		user.setLastName("bai");
		user.getPermissions().add("product:write");
		user.getPermissions().add("order:write");
		String token = util.createToken(user);
		System.out.println(token);
		
		
		
		Claims claims = util.parseJwtClaims(token);

		String sub = claims.getSubject();

		System.out.println(sub);
		// https://jwt.io/#debugger-io
		// https://jwt.io/introduction
	}

}
